﻿using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using TravisCI.Models;
using System.IO.IsolatedStorage;
using System.Runtime.CompilerServices;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Phone.Shell;
using System.Windows.Navigation;
using System.Net;


namespace TravisCI
{
    public class MainViewModel : AbstractViewModel
    {
        private static string RepositoriesKey = "repositories";
        private static string AuthTokenKey = "auth_token";
        private static string TravisTokenKey = "travis_token";
        private static string PrivateRepositoriesKey = "private_repositories";
        private static string DefaultRepo1 = "travis-ci/travis-ci";
        private static string DefaultRepo2 = "travis-ci/travis-worker";
        private IsolatedStorageSettings settings { get; set; }
        private Action runAfterAuthentication;

        public MainViewModel()
        {
            this.settings = IsolatedStorageSettings.ApplicationSettings;
            this.Items = new ObservableCollection<RepositoryViewModel>();
            this.ProgressBarVisible = Visibility.Collapsed;
            this.AuthenticationVisible = Visibility.Collapsed;
            this.MainVisible = Visibility.Visible;
        }

        private Visibility _progressBarVisible;
        private int ActivityCount;
        public Visibility ProgressBarVisible {
            get
            {
                return _progressBarVisible;
            }
            private set
            {
                if (value != _progressBarVisible)
                {
                    _progressBarVisible = value;
                    NotifyPropertyChanged("ProgressBarVisible");
                }
            }
        }

        public ObservableCollection<RepositoryViewModel> Items { get; private set; }

        public bool IsDataLoaded
        {
            get;
            private set;
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void LoadData()
        {
            var repos = new List<string>();
            if (settings.Contains(RepositoriesKey))
            {
                settings.TryGetValue<List<string>>(RepositoriesKey, out repos);
            }
            else
            {
                repos.Add(DefaultRepo1);
                repos.Add(DefaultRepo2);
                settings.Add(RepositoriesKey, repos);
            }

            foreach (var r in repos)
            {
                LoadRepo(r, false);
            }

            if (settings.Contains(PrivateRepositoriesKey))
            {
                settings.TryGetValue<List<string>>(PrivateRepositoriesKey, out repos);
                foreach (var r in repos)
                {
                    LoadRepo(r, true);
                }
            }

            this.IsDataLoaded = true;
        }

        private void LoadRepo(string r, bool dotcom)
        {
            var repo = GetItemForSlug(r);
            if (repo != null)
            {
                ShowProgressBar();
                repo.Repository.LoadBuilds(
                    (_) => HideProgressBar(),
                    (ex) =>
                    {
                        Dispatch(() =>
                        {
                            var msg = "Error retrieving repository " + r + ". Disabling.\n";
                            if (dotcom)
                            {
                                msg += "Was your access token revoked?\n";
                            }
                            MessageBox.Show(msg + "The error was: " + ex.Message);
                        });
                        HideProgressBar();
                    });
            }
            else
            {
                ShowProgressBar();
                if (dotcom)
                {
                    AuthenticatedDo(
                        (token) => FullLoadRepo(r, token, dotcom),
                        () => RemoveRepository(r));
                }
                else
                {
                    try
                    {
                        FullLoadRepo(r, null, false);
                    }
                    catch (Exception)
                    {
                        // Safeguard against broken data
                        var repos = (List<string>)settings[RepositoriesKey];
                        repos.Remove(r);
                        settings[RepositoriesKey] = repos;
                        settings.Save();
                    }
                }
            }
        }

        private void FullLoadRepo(string r, string token, bool dotcom)
        {
            using (var api = new TravisAPI(token, dotcom))
                {
                    api.GetRepository(r, (rep) =>
                    {
                        Dispatch(() =>
                            {
                                Items.Add(new RepositoryViewModel((Repository)rep));
                            });
                        HideProgressBar();
                    },
                    (ex) =>
                    {
                        Dispatch(() =>
                        {
                            var msg = "Error retrieving repository " + r + ". Disabling.\n";
                            if (dotcom)
                            {
                                msg += "Was your access token revoked?\n";
                            }
                            MessageBox.Show(msg + "The error was: " + ex.Message);
                            Items.Add(new RepositoryViewModel(new Repository(token) { Slug = r }));
                        });
                        HideProgressBar();
                    });
                }
        }

        private RepositoryViewModel GetItemForSlug(string slug)
        {
            foreach (var repo in Items)
            {
                if (repo.Slug.Equals(slug))
                {
                    return repo;
                }
            }
            return null;
        }

        public void ShowProgressBar()
        {
            if (++ActivityCount == 1)
            {
                Dispatch(() => ProgressBarVisible = Visibility.Visible);
            }
        }

        public void HideProgressBar()
        {
            if (--ActivityCount == 0)
            {
                Dispatch(() => ProgressBarVisible = Visibility.Collapsed);
            }
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void AddRepository(string slug)
        {
            AddRepository(slug, RepositoriesKey, false);
        }
        
        [MethodImpl(MethodImplOptions.Synchronized)]
        public void AddPrivateRepository(string slug)
        {
            AddRepository(slug, PrivateRepositoriesKey, true);
        }

        private void AddRepository(string slug, string key, bool dotcom)
        {
            var repos = new List<string>();
            if (settings.Contains(key))
            {
                settings.TryGetValue<List<string>>(key, out repos);
            }
            if (!repos.Contains(slug))
            {
                repos.Add(slug);
                settings[key] = repos;
                settings.Save();
                LoadRepo(slug, dotcom);
            }
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void RemoveRepository(string slug)
        {
            List<string> repos;
            if (settings.Contains(RepositoriesKey) && (repos = (List<string>)settings[RepositoriesKey]).Contains(slug))
            {
                repos.Remove(slug);
                settings[RepositoriesKey] = repos;
                settings.Save();
            }
            else if (settings.Contains(PrivateRepositoriesKey) && (repos = (List<string>)settings[PrivateRepositoriesKey]).Contains(slug))
            {
                repos.Remove(slug);
                settings[PrivateRepositoriesKey] = repos;
                settings.Save();
            }

            var repo = GetItemForSlug(slug);
            if (repo != null)
            {
                Dispatch(() => Items.Remove(repo));
            }
        }

        public void EnsureAuthentication(Action cb)
        {
            if (!settings.Contains(AuthTokenKey))
            {
                cb.Invoke();
            }
        }

        public void AuthenticatedDo(Action<string> cb, Action cancel)
        {
            if (settings.Contains(AuthTokenKey))
            {
                AuthenticatedToTravisDo(cb, cancel);
            }
            else
            {
                AuthenticatedToGithubDo(() => AuthenticatedToTravisDo(cb, cancel), cancel);
            }
        }

        private void AuthenticatedToGithubDo(Action cb, Action cancel)
        {
            ToggleAuthenticationVisiblity();
            runAfterAuthentication = cb;
            runAfterCancelAuthentication = cancel;
        }

        private void AuthenticatedToTravisDo(Action<string> cb, Action cancel)
        {
            if (PhoneApplicationService.Current.State.ContainsKey(TravisTokenKey))
            {
                cb.Invoke((string)PhoneApplicationService.Current.State[TravisTokenKey]);
            }
            else
            {
                using (var api = new TravisAPI(null, true)) // FIXME: Remove assumption that only Pro needs auth
                {
                    api.AuthenticateToTravis((string)settings[AuthTokenKey],
                        (tok) =>
                        {
                            PhoneApplicationService.Current.State[TravisTokenKey] = tok;
                            cb.Invoke(tok);
                        },
                        (ex) =>
                        {
                            Dispatch(() => MessageBox.Show("There was an error generating a Travis access token. Was your GitHub token revoked?"));
                            Dispatch(() => cancel.Invoke());
                            HideProgressBar();
                        });
                }
            }
        }

        public void SaveGithubToken(string token)
        {
            settings[AuthTokenKey] = token;
            settings.Save();
            SetGithubTokenButtonState();
        }

        public void ResetGithubToken()
        {
            if (settings.Contains(AuthTokenKey))
            {
                settings.Remove(AuthTokenKey);
                settings.Save();
            }
            SetGithubTokenButtonState();
        }

        internal void CancelAuthentication()
        {
            if (runAfterCancelAuthentication != null)
            {
                Dispatch(() => runAfterCancelAuthentication.Invoke());
            }
            ToggleAuthenticationVisiblity();
            HideProgressBar();
        }

        internal void AuthenticateClick(string username, string password)
        {
            using (var api = new TravisAPI())
            {
                api.AuthenticateToGithub(username, password, (token) =>
                {
                    SaveGithubToken(token);
                    ToggleAuthenticationVisiblity();
                    if (runAfterAuthentication != null)
                    {
                        var cb = runAfterAuthentication;
                        runAfterAuthentication = null;
                        cb.Invoke();
                    }
                },
                (ex) =>
                {
                    Dispatch(() => MessageBox.Show("Cannot create a GitHub access token.\n" + ex.Message));
                    ResetGithubToken();
                    // ToggleAuthenticationVisiblity();
                    HideProgressBar();
                });
            }
        }

        private void ToggleAuthenticationVisiblity()
        {
            Dispatch(() =>
                {
                    AuthenticationVisible = MainVisible;
                    MainVisible = MainVisible == Visibility.Collapsed ? Visibility.Visible : Visibility.Collapsed;
                });
        }

        private Visibility _mainVisible;
        public Visibility MainVisible {
            get
            {
                return _mainVisible;
            }
            set
            {
                if (_mainVisible != value)
                {
                    _mainVisible = value;
                    NotifyPropertyChanged("MainVisible");
                }
            }
        }

        public ApplicationBarMenuItem ResetGithubTokenButton { private get; set; }

        private Visibility _authenticationVisible;
        private Action runAfterCancelAuthentication;
        public Visibility AuthenticationVisible
        {
            get
            {
                return _authenticationVisible;
            }
            set
            {
                if (_authenticationVisible != value)
                {
                    _authenticationVisible = value;
                    NotifyPropertyChanged("AuthenticationVisible");
                }
            }
        }

        internal void SetGithubTokenButtonState()
        {
            Dispatch(() => ResetGithubTokenButton.IsEnabled = settings.Contains(AuthTokenKey));
        }
    }
}